#include "CursorTextureDrawAble.h"
# include "IndexBufferBindAble.h"
# include "VertexBufferBindAble.h"
# include "VertexScalBufferBindAble.h"
# include "VertexShaderBindAble.h"
# include "PixelShaderBindAble.h"
# include "AnisotropicSamplerBindAble.h"

CursorTextureDrawAble::CursorTextureDrawAble(MyDx11* _myDx11, int _width, int _height, float _base_x, float _base_y) : TextureDrawAble(_myDx11, _width, _height, true), m_base_x(_base_x), m_base_y(_base_y)
{
	ID3D11Device* _device = this->getDevice(_myDx11);

	// 顶点缓存
	const Vertex vertices[] = {
		{-1,	1,	0,	0,	0},
		{1,		1,	0,	1,	0},
		{1,		-1,	0,	1,	1},
		{-1,	-1,	0,	0,	1},
	};
	this->insertBindAble(new VertexBufferBindAble(_device, vertices, sizeof(vertices)));

	// 缩放矩阵
	this->m_vertex_scal_buffer = new VertexScalBufferBindAble(_device, { 0.0f, 0.0f, 0.0f}, { this->m_width / this->m_base_x, this->m_height / this->m_base_y, 0.0f});
	this->insertBindAble(this->m_vertex_scal_buffer);

	// 顶点索引
	const UINT16 indices[] = {
		0,1,2, 0,2,3
	};
	this->insertBindAble(new IndexBufferBindAble(_device, indices, sizeof(indices)));
	this->m_index_size = sizeof(indices) / sizeof(UINT16);

	// 顶点着色器
	this->insertBindAble(new VertexShaderBindAble(_device));

	// 像素着色器
	this->insertBindAble(new PixelShaderBindAble(_device));

	// 采样器
	this->insertBindAble(new AnisotropicSamplerBindAble(_device));
}

void CursorTextureDrawAble::draw(MyDx11* _myDx11)
{
	this->drawCursor(_myDx11);
	TextureDrawAble::draw(_myDx11);
}

CursorTextureDrawAble::~CursorTextureDrawAble()
{

}

void CursorTextureDrawAble::drawCursor(MyDx11* _myDx11)
{
	CURSORINFO ci = { 0 };
	ci.cbSize = sizeof(ci);
	if (!GetCursorInfo(&ci)) {
		return;
	}

	float offset_x = (ci.ptScreenPos.x - this->m_x_hotsopt - this->m_base_x / 2);
	float offset_y = (ci.ptScreenPos.y - this->m_y_hotsopt - this->m_base_y / 2);

	this->m_vertex_scal_buffer->changeTranslation(this->getContext(_myDx11), offset_x, offset_y);
	if (this->m_cache_cursor == nullptr || this->m_cache_cursor != ci.hCursor) {
		this->m_cache_cursor = ci.hCursor;
	}
	else {
		return;
	}
	HICON icon = CopyIcon(ci.hCursor);
	ICONINFO ii;
	if (!GetIconInfo(icon, &ii)) {
		return;
	}
	this->m_x_hotsopt = ii.xHotspot;
	this->m_y_hotsopt = ii.yHotspot;
	// copy_from_color
	BITMAP bmp;
	if (!this->copyFromColor(_myDx11, ii, bmp)) {
		this->copyFromMask(_myDx11, ii, bmp);
	}
	DeleteObject(ii.hbmColor);
	DeleteObject(ii.hbmMask);
	DestroyIcon(icon);
}

bool CursorTextureDrawAble::copyFromColor(MyDx11* _myDx11, ICONINFO& _icon_info, BITMAP& _bmp)
{
	uint8_t* output = nullptr;
	if (GetObject(_icon_info.hbmColor, sizeof(_bmp), &_bmp) == 0) {
		return false;
	}
	unsigned int size = _bmp.bmHeight * _bmp.bmWidthBytes;
	output = new uint8_t[size];
	GetBitmapBits(_icon_info.hbmColor, size, output);
	if (_bmp.bmWidth != this->m_width && _bmp.bmHeight != this->m_height) {
		this->changeSize(_myDx11, _bmp.bmWidth, _bmp.bmHeight, false);
	}
	this->copyData(_myDx11, output, _bmp.bmWidthBytes);
	delete[] output;
	return true;
}

bool CursorTextureDrawAble::copyFromMask(MyDx11* _myDx11, ICONINFO& _icon_info, BITMAP& _bmp)
{
	long pixels;
	long bottom;
	uint8_t pix_byte;
	bool alpha = false;
	if (GetObject(_icon_info.hbmMask, sizeof(_bmp), &_bmp) == 0) {
		return false;
	}
	uint8_t* output;
	uint8_t* mask;
	unsigned int size = _bmp.bmHeight * _bmp.bmWidthBytes;
	mask = new uint8_t[size];
	GetBitmapBits(_icon_info.hbmMask, size, mask);
	_bmp.bmHeight /= 2;
	pixels = _bmp.bmHeight * _bmp.bmWidth;
	output = new uint8_t[pixels * 4];
	ZeroMemory(output, pixels * 4);
	bottom = _bmp.bmWidthBytes * _bmp.bmHeight;
	for (long i = 0; i < pixels; i++) {
		uint8_t andMask = this->bitToAlpha(mask, i);
		uint8_t xorMask = this->bitToAlpha(mask + bottom, i);
		if (!andMask) {
			// black in the AND mask
			*(uint32_t*)&output[i * 4] =
				!!xorMask ? 0x00FFFFFF /*always white*/
				: 0xFF000000 /*always black*/;
		}
		else {
			// white in the AND mask
			*(uint32_t*)&output[i * 4] =
				!!xorMask ? 0xFFFFFFFF /*source inverted*/
				: 0 /*transparent*/;
		}
	}
	if (_bmp.bmWidth != this->m_width && _bmp.bmHeight != this->m_height) {
		this->changeSize(_myDx11, _bmp.bmWidth, _bmp.bmHeight, false);
	}
	this->copyData(_myDx11, output, _bmp.bmWidth * 4);
	delete[] output;
	delete[] mask;
	return true;
}

uint8_t CursorTextureDrawAble::bitToAlpha(uint8_t* data, long pixel)
{
	uint8_t pix_byte = data[pixel / 8];
	bool alpha = (pix_byte >> (7 - pixel % 8) & 1) != 0;
	return alpha ? 0xFF : 0;
}
